package main

import (
	"bufio"
	"crypto/sha256"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"sync"
	"time"
)

var paramString_101 string = "{\"id\":101,\"wid\":\"wifiid\",\"ver\":\"9.9.9\"}"

var paramString_901 string = "{\"id\":901,\"token\":\"tokenv\",\"status\":[{\"msgsubtype\":0,\"msgtype\":30,\"result\":\"hAU=\"},{\"msgsubtype\":0,\"msgtype\":102,\"result\":\"9PUBQEkBAP4BAQEBAGYAAQEAIBsdHICAgAAAAAAAAQEBAQIAAAAAAAAAAAAdGSUl+gDkgACAAAAAAAE/AAAAAAAAAAAAAAAAAAAAAIAACA30+w==\"}],\"err\":0}"

var paramString_102 string = "{\"id\":102,\"token\":\"tokenv\"}"

var paramString_103 string = "{\"id\":103,\"token\":\"tokenv\"}"

func SHA256(key []byte) string {
	hash := sha256.New()
	hash.Write(key)
	md := hash.Sum(nil)
	return hex.EncodeToString(md)
}

func Sha256Encode(_encryptstr string) (_encoderet string, err error) {
	sha256str1 := SHA256([]byte(_encryptstr))
	_encoderet = sha256str1
	return _encoderet, nil
}

func main() {
	encryptKey := "8e4b2f6f5fc5e764c38aba5f57a1fdd4"
	wifiId := "8650401000100010040000000e4eeeeeeeee"
	serverAddr := "dev-ac-eu-link.hijuconn.com:443"
	loopNum := 1
	reqCount := 100
	maxSleepInt := 10
	heartbeat := 5
	sleepInterval := 100

	fmt.Println("***************start********************")
	var waitG = &sync.WaitGroup{}
	begin := fmt.Sprintf("%v", time.Now().Format(time.RFC3339Nano))
	for i := 1; i <= loopNum; i++ {
		waitG.Add(i)
		fmt.Println(fmt.Sprintf("begin to connect to dev env:%s for wifiId %s", serverAddr, wifiId))
		go terminal(serverAddr, i, reqCount, maxSleepInt, heartbeat, wifiId, encryptKey, waitG)
		time.Sleep(time.Duration(sleepInterval*1000) * time.Nanosecond)
	}
	println(fmt.Sprintf("begin at: %v, finish at: %v", begin, time.Now().Format(time.RFC3339)))
	waitG.Wait()
	fmt.Println("***************shutdown********************")
}

func terminal(serverAddr string, num, count, maxSleep, heartbeatSleep int, wifiId, encryptKey string, waitG *sync.WaitGroup) {
	defer func() {
		if pErr := recover(); pErr != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have panic err:%+v", time.Now(), num, wifiId, pErr))
		}
	}()

	defer func() {
		waitG.Done()
	}()
	//var sendChan chan string = make(chan string, 64)
	errConnectCount := 0
	connectCount := 0
	var sendChan = make(chan string, 64)
	for {
		var connTls *tls.Conn
		var connTcp net.Conn
		var err error
		conf := &tls.Config{
			InsecureSkipVerify: true,
		}
		connTls, err = tls.Dial("tcp", serverAddr, conf)
		if err != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have connect err:%+v. connect count:%d,%d", time.Now(), num, wifiId, err, errConnectCount, connectCount))
			return
		}
		connectCount++

		var bw *bufio.Writer
		var br *bufio.Reader
		if connTls != nil {
			bw = bufio.NewWriter(connTls)
			br = bufio.NewReader(connTls)
		} else {
			bw = bufio.NewWriter(connTcp)
			br = bufio.NewReader(connTcp)
		}

		token, err := send101Message(bw, br, wifiId, encryptKey)
		if token == "" {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 101 err:%+v", time.Now(), num, wifiId, err))
			return
		}
		err = send102Message(token, encryptKey, sendChan)
		if err != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 102 err:%+v", time.Now(), num, wifiId, err))
		}
		err = send103Message(token, encryptKey, sendChan)
		if err != nil {
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 103 err:%+v", time.Now(), num, wifiId, err))
		}
		var isClose bool = false
		var waitG1 *sync.WaitGroup = &sync.WaitGroup{}
		waitG1.Add(1)
		go func() {
			for {
				if isClose {
					break
				}
				err = heartbeat(sendChan)
				if err != nil {
					fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 902 err:%+v", time.Now(), num, wifiId, err))
				}
				if isClose {
					break
				}
				time.Sleep(time.Duration(heartbeatSleep) * time.Second)
			}
			waitG1.Done()
		}()

		waitG1.Add(2)
		go func() {
			for i := 0; i < count; i++ {
				if isClose {
					break
				}
				err := send901Message(token, encryptKey, sendChan)
				if err != nil {
					fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have 901 err:%+v", time.Now(), num, wifiId, err))
				}
				if isClose {
					break
				}
				sleepTime := rand.Intn(maxSleep) + 1
				time.Sleep(time.Duration(sleepTime) * time.Second)
			}
			fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s finish 901 send", time.Now(), num, wifiId))
			isClose = true
			waitG1.Done()
		}()
		go func() {
			for {
				var mesage string
				mesage = <-sendChan
				bw.WriteString(mesage)
				bw.Flush()
				if strings.Contains(mesage, "\"id\":902") || strings.Contains(mesage, "\"id\":102") || strings.Contains(mesage, "\"id\":103") {
					if connTls != nil {
						connTls.SetReadDeadline(time.Now().Add(5 * time.Second))
					} else {
						connTcp.SetReadDeadline(time.Now().Add(5 * time.Second))
					}
					_, err := br.ReadString('\n')
					if err != nil {
						fmt.Println(fmt.Sprintf("%+v=>terminal num:%d,wifiId:%s have send err:%+v, send message:%+v", time.Now(), num, wifiId, err, mesage))
						isClose = true
						errConnectCount++
					}
				}
			}
		}()
		waitG1.Wait()
		if connTls != nil {
			connTls.Close()
		} else {
			connTcp.Close()
		}
	}
}

func send101Message(bw *bufio.Writer, br *bufio.Reader, wifiId, encryptKey string) (string, error) {
	param := strings.Replace(paramString_101, "wifiid", wifiId, -1)
	md5Sign, err := Sha256Encode(encryptKey + param)
	if err != nil {
		return "", err
	}
	param101 := "HS" + param + md5Sign + "\r\n"
	bw.WriteString(param101)
	bw.Flush()
	fmt.Println(fmt.Sprintf("%+v=>sent 101 request: %s of wifiId %s to ac-julink", time.Now(), param101, wifiId))
	response, err := br.ReadString('\n')
	fmt.Println(fmt.Sprintf("%+v=>receive 101 response: %s of wifiId %s to ac-julink", time.Now(), response, wifiId))
	if err != nil {
		return "", err
	}
	var token string
	if strings.Contains(response, "token") {
		resArray := strings.Split(response, ",")
		for _, element := range resArray {
			if strings.Contains(element, "token") {
				token = strings.Replace(strings.Replace(element, "\"token\":\"", "", -1), "\"", "", -1)
				if strings.Contains(token, "}") {
					arr := strings.Split(token, "}")
					token = arr[0]
				}
				break
			}
		}
		return token, nil
	} else {
		return "", errors.New("101 response is empty")
	}
}

func send901Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_901, "tokenv", token, -1)
	md5Sign, err := Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param901 := "HS" + param + md5Sign + "\r\n"
	fmt.Println(fmt.Sprintf("%+v=>sent 901 request: %s of token %s to ac-julink", time.Now(), param901, token))
	sendChan <- param901
	return nil
}

func send102Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_102, "tokenv", token, -1)
	md5Sign, err := Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param102 := "HS" + param + md5Sign + "\r\n"
	fmt.Println(fmt.Sprintf("%+v=>sent 102 request: %s of token %s to ac-julink", time.Now(), param102, token))
	sendChan <- param102
	return nil
}

func send103Message(token, encryptKey string, sendChan chan string) error {
	param := strings.Replace(paramString_103, "tokenv", token, -1)
	md5Sign, err := Sha256Encode(encryptKey + param)
	if err != nil {
		return err
	}
	param103 := "HS" + param + md5Sign + "\r\n"
	fmt.Println(fmt.Sprintf("%+v=>sent 103 request: %s of token %s to ac-julink", time.Now(), param103, token))
	sendChan <- param103
	return nil
}

func heartbeat(sendChan chan string) error {
	param902 := "HS{\"id\":902}\r\n"
	fmt.Println(fmt.Sprintf("%+v=>sent 902 heartbeat request: %s to ac-julink", time.Now(), param902))
	sendChan <- param902
	return nil
}
